/**
 * 
 */
package org.statewalker.parser.xhtml.serializer;

import java.util.Map;

import org.statewalker.parser.xhtml.grammar.XHTMLCharactersEntities;
import org.statewalker.parser.xhtml.grammar.XHTMLSpecialEntities;
import org.statewalker.parser.xhtml.grammar.XHTMLSymbolsEntities;
import org.statewalker.tokenizer.CharStream;
import org.statewalker.tokenizer.xml.Entity;
import org.statewalker.tokenizer.xml.EntityFactory;
import org.statewalker.tokenizer.xml.EntityReader;

/**
 * @author kotelnikov
 */
public class XmlPrinter implements IXmlListener {

    private EntityReader fEntityReader;

    /**
     * 
     */
    public XmlPrinter() {
        EntityFactory entityFactory = new EntityFactory();
        new XHTMLCharactersEntities(entityFactory);
        new XHTMLSymbolsEntities(entityFactory);
        new XHTMLSpecialEntities(entityFactory);
        fEntityReader = new EntityReader(entityFactory, true);
    }

    /**
     * @see org.statewalker.parser.xhtml.serializer.IXmlListener#beginTag(java.lang.String,
     *      java.util.Map)
     */
    public void beginTag(String tagName, Map<String, String> attributes) {
        print("<");
        print(tagName);
        printAttributes(attributes);
        print(">");
    }

    /**
     * @see org.statewalker.parser.xhtml.serializer.IXmlListener#endTag(java.lang.String)
     */
    public void endTag(String tagName) {
        print("</");
        print(tagName);
        print(">");
    }

    private String escapeKey(String key) {
        return key;
    }

    private String escapeValue(String value) {
        if ("".equals(value))
            return value;
        StringBuilder buf = new StringBuilder();
        CharStream stream = new CharStream(value);
        while (!stream.isTerminated()) {
            char ch = stream.getChar();
            switch (ch) {
                case '&':
                    Entity entity = fEntityReader.readEntity(stream);
                    if (entity != null) {
                        buf.append(entity.toString());
                        continue;
                    } else {
                        buf.append("&#38;");
                    }
                    break;
                case '\"':
                    buf.append("&#34;");
                    break;
                default:
                    buf.append(ch);
                    break;
            }
            if (!stream.incPos())
                break;
        }
        return buf.toString();
    }

    /**
     * @see org.statewalker.parser.xhtml.serializer.IXmlListener#onCDATA(java.lang.String)
     */
    public void onCDATA(String content) {
        print("<![CDATA[");
        print(content);
        print("]]>");
    }

    /**
     * @see org.statewalker.parser.xhtml.serializer.IXmlListener#onComment(java.lang.String)
     */
    public void onComment(String comment) {
        print("<!-- ");
        print(comment);
        print(" -->");
    }

    /**
     * @see org.statewalker.parser.xhtml.serializer.IXmlListener#onEmptyTag(java.lang.String,
     *      java.util.Map)
     */
    public void onEmptyTag(String tagName, Map<String, String> attributes) {
        print("<");
        print(tagName);
        printAttributes(attributes);
        print(" />");
    }

    public void onExtraSpaces(String str) {
        print(str);
    }

    /**
     * @see org.statewalker.parser.xhtml.serializer.IXmlListener#onText(java.lang.String)
     */
    public void onText(String text) {
        print(text);
    }

    protected void print(String string) {
        System.out.print(string);
    }

    protected void printAttributes(Map<String, String> attributes) {
        for (Map.Entry<String, String> attr : attributes.entrySet()) {
            print(" ");
            String key = attr.getKey();
            String value = attr.getValue();
            print(escapeKey(key));
            print("=\"");
            print(escapeValue(value));
            print("\"");
        }
    }

}
